#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include "pal/lane_control.h"
#include "CxlUtil.h"
#include "userLog.h"
#include "pal/lane_cmd_parse.h"
#include "common/configParser.h"
#include "voiceChedaoji.h"
#include "pending_data_queue.h"
#include "hal/imx6/src/hal_gps_uv.h"

// 服务器重连间隔
#define LANE_RECONNECT_TIME_OUT 6 * 1000

static void lane_recv_response(uv_stream_t *clientStream, ssize_t nread, const uv_buf_t *buf);

static uint8_t recvResponseCount = 0;
static bool isConnectToServ = false;

static uv_async_t asyncSendPendingWork;
static void sendPendingQueueWork(uv_async_t *handle);

static void uv_write_with_test_connecting(uint8_t *buf, uint32_t len)
{
    write_req_t *wr_rq = (write_req_t *)malloc(sizeof(write_req_t));
    wr_rq->buf = uv_buf_init(buf, len);
    if (getIsConnectToServ() == true)
    {
        uv_connect_t *client = getLaneClientConnect();
        uv_write((uv_write_t *)wr_rq, client->handle, &wr_rq->buf, 1, lane_write_cb);
    }
    else
    {
        // LOG_ERROR("未连接至服务器，不写socket");
        lane_write_cb((uv_write_t *)wr_rq, 0);
    }
}

void dispatchSendPendingQueue()
{
    uv_async_send(&asyncSendPendingWork);
}

bool getIsConnectToServ()
{
    return isConnectToServ;
}

// 初始化帧头部
void headInit(head_t *head, uint8_t cmd)
{
    head->head = htons(0x555A);
    head->station_no = htonl(getParkId());
    memcpy(head->device_no, getChedaojiNo(), MEM_SIZE(head_t, device_no));
    memset(head->head_reserve, 0xff, MEM_SIZE(head_t, head_reserve));
    head->cmd = cmd;
}

// 设备静态参数初始化
static void deviceArgInit(device_arg_t *arg)
{
    memset(arg, 0, sizeof(device_arg_t));
}

// 设备运行参数初始化
static void deviceRunningInit(device_running_t *arg)
{
    memset(arg, 0, sizeof(device_running_t));
    time_bcd(arg->device_timeBcd);
}

// 注册帧初始化
static void registetFrameInit(registration_t *reg)
{
    memset(reg, 0, sizeof(registration_t));
    headInit((head_t *)reg, REGISTER_CMD);
    deviceArgInit((device_arg_t *)((uint8_t *)reg + sizeof(head_t)));
    // 版本号
    reg->register_version = 0x0;
    memset(reg->encrypt_key, 0x0, MEM_SIZE(registration_t, encrypt_key));
    memset(reg->register_reserve, 0xff, MEM_SIZE(registration_t, register_reserve));
    reg->len = CAL_FRAME_DATA_LEN_TO_NET(registration_t);
    reg->crc = htons(crc((uint8_t *)reg + CRC_BASE_ADDR(registration_t), sizeof(registration_t) - MEM_SIZE(registration_t, crc) - MEM_SIZE(registration_t, head)));
}

// 心跳包初始化
static void heartBeatFrameInit(heart_beat_t *heartBeat)
{
    memset(heartBeat, 0, sizeof(heart_beat_t));
    headInit((head_t *)heartBeat, HEART_BEAT_CMD);
    deviceRunningInit((device_running_t *)((uint8_t *)heartBeat + sizeof(head_t)));
    time_bcd(heartBeat->heartbeat_timeBcd);

    gps_t gps = getLastGps();
    memcpy(heartBeat->gps_gngga_data, gps.gps_gngga_data, MINMEA_MAX_LENGTH);
    memcpy(heartBeat->gps_gnrmc_data, gps.gps_gnrmc_data, MINMEA_MAX_LENGTH);

    heartBeat->len = CAL_FRAME_DATA_LEN_TO_NET(heart_beat_t);
    heartBeat->crc = htons(crc((uint8_t *)heartBeat + CRC_BASE_ADDR(heart_beat_t), sizeof(heart_beat_t) - MEM_SIZE(heart_beat_t, crc) - MEM_SIZE(heart_beat_t, head)));
}

static void closeAndReconnect()
{
    isConnectToServ = false;
    if (uv_is_closing((uv_handle_t *)getLaneClientConnect()->handle) == 0)
    {
        uv_close((uv_handle_t *)getLaneClientConnect()->handle, NULL);
    }
    laneClearInterval();
    laneMainWorkSetTimeout(laneAppInit, LANE_RECONNECT_TIME_OUT);
    recvResponseCount = 0;
}

static void lostConnectHandle(int32_t status)
{
    if (status && isConnectToServ != false)
    {
        const uint8_t *err_str = uv_err_name(status);
        LOG_ERROR("服务器socket写失败，重新链接， 原因：%s", err_str);
        closeAndReconnect();
    }
}

// 服务器写socket回调
void lane_write_cb(uv_write_t *req, int32_t status)
{
    lostConnectHandle(status);
    laneRestart();
    free_write_req(req);
}

// 定时心跳任务回调
void laneHeardBeatTask(uv_timer_t *handle)
{
    heart_beat_t *heatBeat = (heart_beat_t *)malloc(sizeof(heart_beat_t));

    memset(heatBeat, 0, sizeof(heart_beat_t));
    heartBeatFrameInit(heatBeat);
    // 心跳报文打印
    // hzlog_debug(userLog, heatBeat, sizeof(heart_beat_t));

    uv_write_with_test_connecting((uint8_t *)heatBeat, sizeof(heart_beat_t));

    if (recvResponseCount > 3)
    {
        LOG_ERROR("服务器超过%d次心跳无响应，重新链接", recvResponseCount);
        closeAndReconnect();
    }
    recvResponseCount++;
}

// 注册任务回调
static void laneRegisterTask(uv_connect_t *client, int32_t status)
{
    if (status < 0)
    {
        LOG_ERROR("服务器 %s:%d 链接失败， 原因：%s", getRemoteServIp(), getRemoteServPort(), uv_strerror(status));
        laneMainWorkSetTimeout(laneAppInit, LANE_RECONNECT_TIME_OUT);
        return;
    }
    isConnectToServ = true;
    LOG_INFO("连接服务器成功，发送注册信息");

    audioNetConnected();

    // 接收事件注册
    uv_read_start(client->handle, alloc_buffer, lane_recv_response);

    // 发送注册信息至服务器
    laneRegister();
}

static void reRegister(uv_timer_t *handle)
{
    if (isConnectToServ == true)
    {
        LOG_INFO("重新向服务器发起注册");
        laneRegister();
    }
}

void laneRegister()
{
    registration_t *reg = (registration_t *)malloc(sizeof(registration_t));

    memset(reg, 0, sizeof(registration_t));
    registetFrameInit(reg);
    // 注册打印
    // hzlog_info(userLog, reg, sizeof(registration_t));

    uv_write_with_test_connecting((uint8_t *)reg, sizeof(registration_t));

    // 隔一定时间重新注册
    static bool isInitTimer = false;
    static uv_timer_t reRegisterTimer;
    // 每24小时注册一次
    static uint32_t timeOut = 24 * 60 * 60 * 1000;
    if (isInitTimer == false)
    {
        uv_timer_init(getLoop(), &reRegisterTimer);
        isInitTimer = true;
        uv_timer_start(&reRegisterTimer, reRegister, timeOut, timeOut);
    }

    if (uv_timer_get_due_in(&reRegisterTimer) > 0)
    {
        // 每次注册都重置下重新注册的间隔
        uv_timer_again(&reRegisterTimer);
    }
}

void laneAppInit()
{
    audioNetConnecting();
    static bool isInitAsyncPendingWork = false;
    if (isInitAsyncPendingWork == false)
    {
        uv_async_init(getLoop(), &asyncSendPendingWork, sendPendingQueueWork);
        isInitAsyncPendingWork = true;
    }
    uint8_t *servIp = getRemoteServIp();
    uint16_t servPort = getRemoteServPort();
    laneConnect(servIp, servPort, laneRegisterTask);
}

static void lane_recv_response(uv_stream_t *clientStream, ssize_t nread, const uv_buf_t *buf)
{
    if (nread > 0)
    {
        // LOG_INFO("收到服务器报文的内容：");
        // hzlog_info(userLog, buf->base, nread);
        recvResponseCount = 0;
        cmd_parse_queue_work(buf, nread);
    }
    else if (nread < 0)
    {
        if (nread != UV_EOF)
        {
            LOG_ERROR("读服务器socket错误 %s", uv_err_name(nread));
            laneClearInterval();
            uv_close((uv_handle_t *)getLaneClientConnect()->handle, NULL);
            laneMainWorkSetTimeout(laneAppInit, LANE_RECONNECT_TIME_OUT);
        }
    }

    free(buf->base);
}

static void sendPendingQueueWork(uv_async_t *handle)
{
    Packet *packTmp = NULL;
    const uint32_t sendCounts = getPicsQueueLen() + getOthersQueueLen();

    for (size_t i = 0; i < sendCounts; i++)
    {
        bool isOthersEmpty = isOthersQueueEmpty();
        bool isPicsEmpty = isPicsQueueEmpty();

        if (isOthersEmpty == true && isPicsEmpty == true)
        {
            break;
        }
        else
        {
            if (isOthersEmpty != true)
            {
                packTmp = othersDequeue();
            }
            else if (isPicsEmpty != true)
            {
                packTmp = picsDequeue();
                // hzlog_info(userLog, packTmp->pkt_data, packTmp->pkt_len);
            }
            if (packTmp != NULL)
            {
                // LOG_INFO("%d", packTmp->pkt_len);
                // pic_block_send_t blockTmp = {0};
                // memcpy(&blockTmp, packTmp->pkt_data, packTmp->pkt_len);
                // LOG_INFO("分块序号：%d，长度：%d", ntohs(blockTmp.block_no), packTmp->pkt_len);
                uv_write_with_test_connecting((uint8_t *)packTmp->pkt_data, packTmp->pkt_len);
                free(packTmp);
            }
        }
    }

    bool isOthersEmpty = isOthersQueueEmpty();
    bool isPicsEmpty = isPicsQueueEmpty();

    if (isOthersEmpty != true || isPicsEmpty != true)
    {
        dispatchSendPendingQueue();
    }
}
